{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# 从 Spotify 中手机播放列表和歌曲\n",
    "\n",
    "为歌曲推荐系统创建训练集。\n",
    "\n",
    "登陆 Spotify：用 [Spotipy](https://github.com/plamere/spotipy) 客户端"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "import json\n",
    "\n",
    "# create an app on\n",
    "#     https://developer.spotify.com/dashboard/applications\n",
    "# and fill configs in \"spotify-key.json\":\n",
    "#     { \"client_id\": \"...\", \"client_secret\": \"...\" }\n",
    "\n",
    "with open(\"./spotify-key.json\") as f:\n",
    "    _spotify_key = json.load(f)\n",
    "\n",
    "CLIENT_ID = _spotify_key[\"client_id\"]\n",
    "CLIENT_SECRET = _spotify_key[\"client_secret\"]"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import spotipy\n",
    "from spotipy.oauth2 import SpotifyClientCredentials\n",
    "\n",
    "sp = spotipy.Spotify(\n",
    "    auth_manager=SpotifyClientCredentials(client_id=CLIENT_ID,\n",
    "                                          client_secret=CLIENT_SECRET))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "import time\n",
    "from spotipy import SpotifyException\n",
    "\n",
    "\n",
    "def next_page(spotify: spotipy.Spotify, prev_result):\n",
    "    \"\"\"fetch next page\n",
    "\n",
    "    https://spotipy.readthedocs.io/en/2.19.0/#spotipy.client.Spotify.next\n",
    "\n",
    "    :return: result of next page\n",
    "    \"\"\"\n",
    "    tries = 3\n",
    "    while tries > 0:\n",
    "        try:\n",
    "            return spotify.next(prev_result)\n",
    "        except SpotifyException:\n",
    "            tries -= 1\n",
    "            time.sleep(0.2)\n",
    "            if tries <= 0:\n",
    "                raise\n",
    "\n",
    "\n",
    "def find_playlists(spotify: spotipy.Spotify, w: str, limit=50, max_offset=1000):\n",
    "    \"\"\"获取匹配搜索项 w 的所有播放列表\n",
    "\n",
    "    https://developer.spotify.com/documentation/web-api/reference/#/operations/search\n",
    "\n",
    "    :param spotify: spotipy.Spotify 实例\n",
    "    :param w: 要搜索的关键字\n",
    "    :param limit: 一次请求多少(>= 0 <= 50)，\n",
    "        控制内部行为，不是生成 limit 个，该函数生成 limit * max_offset 个\n",
    "    :param max_offset: 你要多少(>= 0 <= 1000)，总量 limit * max_offset\n",
    "    :return: 生成器，每次出一张播放列表\n",
    "    \"\"\"\n",
    "\n",
    "    offset = 0  # counter\n",
    "\n",
    "    try:\n",
    "        res = spotify.search(w, limit=limit, type=\"playlist\")\n",
    "        while res:\n",
    "            if (offset := offset + limit) >= max_offset:  # PEP 572: 3.8+\n",
    "                return\n",
    "\n",
    "            # for playlist in res[\"playlists\"][\"items\"]:\n",
    "            #     yield playlist\n",
    "            yield from res[\"playlists\"][\"items\"]  # PEP 380\n",
    "\n",
    "            res = next_page(spotify, res['playlists'])\n",
    "\n",
    "    except SpotifyException as e:\n",
    "        if e.http_status == 404:\n",
    "            return\n",
    "        raise"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "试一下："
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "source": [
    "i = 0\n",
    "for pl in find_playlists(sp, 'zutomayo'):  # 只是因为写到这行刚好听着 inside joke\n",
    "    print(pl, end=\"\\n---\\n\")\n",
    "    i += 1\n",
    "    if i > 3:\n",
    "        break"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "execution_count": null,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "Ok，可以跑，接下来，干票大的。\n",
    "\n",
    "我们从 1 个单词 \"a\" 开始，获取 5000 个包含这个单词的播放列表。\n",
    "计算这些列表中所有单词出现的频次。然后对列表中出现次数最多的单词执行相同的操作。\n",
    "重复，知道获取到足够的列表。"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "from collections import Counter\n",
    "from gensim.utils import tokenize\n",
    "\n",
    "MAX_COUNT_PLAYLISTS = 200000\n",
    "\n",
    "word_counts = Counter({'a': 1})\n",
    "playlists = {}\n",
    "words_seen = set()\n",
    "playlists = {}\n",
    "count = 0\n",
    "dupes = 0\n",
    "\n",
    "while len(playlists) < MAX_COUNT_PLAYLISTS:\n",
    "    for w, _ in word_counts.most_common():\n",
    "        if w not in words_seen:\n",
    "            break\n",
    "    word = w\n",
    "\n",
    "    words_seen.add(word)\n",
    "    print(f'{count: 7}/{MAX_COUNT_PLAYLISTS: 7}: {count / MAX_COUNT_PLAYLISTS * 100: 2.2f}% > {word}')\n",
    "    for playlist in find_playlists(sp, word):\n",
    "        if playlist['id'] in playlists:\n",
    "            dupes += 1\n",
    "        elif playlist['name'] and playlist['owner']:\n",
    "            playlists[playlist['id']] = {\n",
    "                'owner': playlist['owner']['id'],\n",
    "                'name': playlist['name'],\n",
    "                'id': playlist['id'],\n",
    "            }\n",
    "            count += 1\n",
    "            for token in tokenize(playlist['name'], lowercase=True):\n",
    "                word_counts[token] += 1"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "len(playlists)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "获取列表里的曲目信息："
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%% md\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "def tracks_gen(spotify: spotipy.Spotify, playlist):\n",
    "    \"\"\"Yield tracks in a playlist\n",
    "\n",
    "    :param spotify: Spotify 实例\n",
    "    :param playlist: find_playlists 生成的播放列表\n",
    "    :return: 生成器，一次产出一个 track\n",
    "    \"\"\"\n",
    "    res = spotify.playlist_items(playlist['id'],\n",
    "                                 fields='items(track(id, name, artists(name, id), duration_ms)),next')\n",
    "    while res:\n",
    "        if not res or not res.get('items'):\n",
    "            return\n",
    "\n",
    "        # yield this\n",
    "        for track in res['items']:\n",
    "            if track['track']:\n",
    "                yield track['track']\n",
    "        # fetch next\n",
    "        try:\n",
    "            res = next_page(spotify, res)\n",
    "        except SpotifyException as e:\n",
    "            if 400 <= e.http_status <= 499:\n",
    "                return\n",
    "            raise"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "试一下："
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "p = playlists[(p for p in playlists.keys()).__next__()]\n",
    "\n",
    "print(\"playlist:\", p)\n",
    "print(\"tracks:\")\n",
    "\n",
    "i = 0\n",
    "for t in tracks_gen(sp, p):\n",
    "    print(t)\n",
    "    if (i := i + 1) > 3:\n",
    "        break"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "真的跑了：数据写到数据库里。"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  4%|▍         | 7752/193703 [1:40:42<40:15:51,  1.28it/s] \n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mKeyboardInterrupt\u001B[0m                         Traceback (most recent call last)",
      "\u001B[0;32m/var/folders/6w/jqgyt_fs4nxf0q3rr__n60wc0000gn/T/ipykernel_7714/2163360150.py\u001B[0m in \u001B[0;36m<module>\u001B[0;34m\u001B[0m\n\u001B[1;32m     36\u001B[0m             \u001B[0mtrack_ids\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;34m[\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m     37\u001B[0m             \u001B[0;32mtry\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 38\u001B[0;31m                 \u001B[0;32mfor\u001B[0m \u001B[0mtrack\u001B[0m \u001B[0;32min\u001B[0m \u001B[0mtracks_gen\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0msp\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mplaylist\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m     39\u001B[0m                     \u001B[0mtrack_id\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mtrack\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;34m'id'\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m     40\u001B[0m                     \u001B[0;32mif\u001B[0m \u001B[0;32mnot\u001B[0m \u001B[0mtrack_id\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m/var/folders/6w/jqgyt_fs4nxf0q3rr__n60wc0000gn/T/ipykernel_7714/3756545586.py\u001B[0m in \u001B[0;36mtracks_gen\u001B[0;34m(spotify, playlist)\u001B[0m\n\u001B[1;32m     18\u001B[0m         \u001B[0;31m# fetch next\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m     19\u001B[0m         \u001B[0;32mtry\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 20\u001B[0;31m             \u001B[0mres\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mnext_page\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mspotify\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mres\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m     21\u001B[0m         \u001B[0;32mexcept\u001B[0m \u001B[0mSpotifyException\u001B[0m \u001B[0;32mas\u001B[0m \u001B[0me\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m     22\u001B[0m             \u001B[0;32mif\u001B[0m \u001B[0;36m400\u001B[0m \u001B[0;34m<=\u001B[0m \u001B[0me\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mhttp_status\u001B[0m \u001B[0;34m<=\u001B[0m \u001B[0;36m499\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m/var/folders/6w/jqgyt_fs4nxf0q3rr__n60wc0000gn/T/ipykernel_7714/3807262667.py\u001B[0m in \u001B[0;36mnext_page\u001B[0;34m(spotify, prev_result)\u001B[0m\n\u001B[1;32m     13\u001B[0m     \u001B[0;32mwhile\u001B[0m \u001B[0mtries\u001B[0m \u001B[0;34m>\u001B[0m \u001B[0;36m0\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m     14\u001B[0m         \u001B[0;32mtry\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 15\u001B[0;31m             \u001B[0;32mreturn\u001B[0m \u001B[0mspotify\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mnext\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mprev_result\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m     16\u001B[0m         \u001B[0;32mexcept\u001B[0m \u001B[0mSpotifyException\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m     17\u001B[0m             \u001B[0mtries\u001B[0m \u001B[0;34m-=\u001B[0m \u001B[0;36m1\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/intro-YaGh8CRL/lib/python3.9/site-packages/spotipy/client.py\u001B[0m in \u001B[0;36mnext\u001B[0;34m(self, result)\u001B[0m\n\u001B[1;32m    319\u001B[0m         \"\"\"\n\u001B[1;32m    320\u001B[0m         \u001B[0;32mif\u001B[0m \u001B[0mresult\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;34m\"next\"\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 321\u001B[0;31m             \u001B[0;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_get\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mresult\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;34m\"next\"\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m    322\u001B[0m         \u001B[0;32melse\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    323\u001B[0m             \u001B[0;32mreturn\u001B[0m \u001B[0;32mNone\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/intro-YaGh8CRL/lib/python3.9/site-packages/spotipy/client.py\u001B[0m in \u001B[0;36m_get\u001B[0;34m(self, url, args, payload, **kwargs)\u001B[0m\n\u001B[1;32m    295\u001B[0m             \u001B[0mkwargs\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    296\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 297\u001B[0;31m         \u001B[0;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_internal_call\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m\"GET\"\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0murl\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mpayload\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mkwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m    298\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    299\u001B[0m     \u001B[0;32mdef\u001B[0m \u001B[0m_post\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0murl\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0margs\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0;32mNone\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mpayload\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0;32mNone\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/intro-YaGh8CRL/lib/python3.9/site-packages/spotipy/client.py\u001B[0m in \u001B[0;36m_internal_call\u001B[0;34m(self, method, url, payload, params)\u001B[0m\n\u001B[1;32m    238\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    239\u001B[0m         \u001B[0;32mtry\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 240\u001B[0;31m             response = self._session.request(\n\u001B[0m\u001B[1;32m    241\u001B[0m                 \u001B[0mmethod\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0murl\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mheaders\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mheaders\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mproxies\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mproxies\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    242\u001B[0m                 \u001B[0mtimeout\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mrequests_timeout\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/intro-YaGh8CRL/lib/python3.9/site-packages/requests/sessions.py\u001B[0m in \u001B[0;36mrequest\u001B[0;34m(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)\u001B[0m\n\u001B[1;32m    527\u001B[0m         }\n\u001B[1;32m    528\u001B[0m         \u001B[0msend_kwargs\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mupdate\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0msettings\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 529\u001B[0;31m         \u001B[0mresp\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0msend\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mprep\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0msend_kwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m    530\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    531\u001B[0m         \u001B[0;32mreturn\u001B[0m \u001B[0mresp\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/intro-YaGh8CRL/lib/python3.9/site-packages/requests/sessions.py\u001B[0m in \u001B[0;36msend\u001B[0;34m(self, request, **kwargs)\u001B[0m\n\u001B[1;32m    643\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    644\u001B[0m         \u001B[0;31m# Send the request\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 645\u001B[0;31m         \u001B[0mr\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0madapter\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0msend\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mrequest\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m    646\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    647\u001B[0m         \u001B[0;31m# Total elapsed time of the request (approximately)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/intro-YaGh8CRL/lib/python3.9/site-packages/requests/adapters.py\u001B[0m in \u001B[0;36msend\u001B[0;34m(self, request, stream, timeout, verify, cert, proxies)\u001B[0m\n\u001B[1;32m    438\u001B[0m         \u001B[0;32mtry\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    439\u001B[0m             \u001B[0;32mif\u001B[0m \u001B[0;32mnot\u001B[0m \u001B[0mchunked\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 440\u001B[0;31m                 resp = conn.urlopen(\n\u001B[0m\u001B[1;32m    441\u001B[0m                     \u001B[0mmethod\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mrequest\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mmethod\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    442\u001B[0m                     \u001B[0murl\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0murl\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/intro-YaGh8CRL/lib/python3.9/site-packages/urllib3/connectionpool.py\u001B[0m in \u001B[0;36murlopen\u001B[0;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)\u001B[0m\n\u001B[1;32m    701\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    702\u001B[0m             \u001B[0;31m# Make the request on the httplib connection object.\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 703\u001B[0;31m             httplib_response = self._make_request(\n\u001B[0m\u001B[1;32m    704\u001B[0m                 \u001B[0mconn\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    705\u001B[0m                 \u001B[0mmethod\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/intro-YaGh8CRL/lib/python3.9/site-packages/urllib3/connectionpool.py\u001B[0m in \u001B[0;36m_make_request\u001B[0;34m(self, conn, method, url, timeout, chunked, **httplib_request_kw)\u001B[0m\n\u001B[1;32m    447\u001B[0m                     \u001B[0;31m# Python 3 (including for exceptions like SystemExit).\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    448\u001B[0m                     \u001B[0;31m# Otherwise it looks like a bug in the code.\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 449\u001B[0;31m                     \u001B[0msix\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mraise_from\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0me\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;32mNone\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m    450\u001B[0m         \u001B[0;32mexcept\u001B[0m \u001B[0;34m(\u001B[0m\u001B[0mSocketTimeout\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mBaseSSLError\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mSocketError\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;32mas\u001B[0m \u001B[0me\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    451\u001B[0m             \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_raise_timeout\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0merr\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0me\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0murl\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0murl\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mtimeout_value\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mread_timeout\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/intro-YaGh8CRL/lib/python3.9/site-packages/urllib3/packages/six.py\u001B[0m in \u001B[0;36mraise_from\u001B[0;34m(value, from_value)\u001B[0m\n",
      "\u001B[0;32m~/.local/share/virtualenvs/intro-YaGh8CRL/lib/python3.9/site-packages/urllib3/connectionpool.py\u001B[0m in \u001B[0;36m_make_request\u001B[0;34m(self, conn, method, url, timeout, chunked, **httplib_request_kw)\u001B[0m\n\u001B[1;32m    442\u001B[0m                 \u001B[0;31m# Python 3\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    443\u001B[0m                 \u001B[0;32mtry\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 444\u001B[0;31m                     \u001B[0mhttplib_response\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mconn\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mgetresponse\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m    445\u001B[0m                 \u001B[0;32mexcept\u001B[0m \u001B[0mBaseException\u001B[0m \u001B[0;32mas\u001B[0m \u001B[0me\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    446\u001B[0m                     \u001B[0;31m# Remove the TypeError from the exception chain in\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m/usr/local/Cellar/python@3.9/3.9.9/Frameworks/Python.framework/Versions/3.9/lib/python3.9/http/client.py\u001B[0m in \u001B[0;36mgetresponse\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m   1375\u001B[0m         \u001B[0;32mtry\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m   1376\u001B[0m             \u001B[0;32mtry\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m-> 1377\u001B[0;31m                 \u001B[0mresponse\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mbegin\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m   1378\u001B[0m             \u001B[0;32mexcept\u001B[0m \u001B[0mConnectionError\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m   1379\u001B[0m                 \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mclose\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m/usr/local/Cellar/python@3.9/3.9.9/Frameworks/Python.framework/Versions/3.9/lib/python3.9/http/client.py\u001B[0m in \u001B[0;36mbegin\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m    318\u001B[0m         \u001B[0;31m# read until we get a non-100 response\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    319\u001B[0m         \u001B[0;32mwhile\u001B[0m \u001B[0;32mTrue\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 320\u001B[0;31m             \u001B[0mversion\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mstatus\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mreason\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_read_status\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m    321\u001B[0m             \u001B[0;32mif\u001B[0m \u001B[0mstatus\u001B[0m \u001B[0;34m!=\u001B[0m \u001B[0mCONTINUE\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    322\u001B[0m                 \u001B[0;32mbreak\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m/usr/local/Cellar/python@3.9/3.9.9/Frameworks/Python.framework/Versions/3.9/lib/python3.9/http/client.py\u001B[0m in \u001B[0;36m_read_status\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m    279\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    280\u001B[0m     \u001B[0;32mdef\u001B[0m \u001B[0m_read_status\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 281\u001B[0;31m         \u001B[0mline\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mstr\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mfp\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mreadline\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0m_MAXLINE\u001B[0m \u001B[0;34m+\u001B[0m \u001B[0;36m1\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m\"iso-8859-1\"\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m    282\u001B[0m         \u001B[0;32mif\u001B[0m \u001B[0mlen\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mline\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;34m>\u001B[0m \u001B[0m_MAXLINE\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    283\u001B[0m             \u001B[0;32mraise\u001B[0m \u001B[0mLineTooLong\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m\"status line\"\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m/usr/local/Cellar/python@3.9/3.9.9/Frameworks/Python.framework/Versions/3.9/lib/python3.9/socket.py\u001B[0m in \u001B[0;36mreadinto\u001B[0;34m(self, b)\u001B[0m\n\u001B[1;32m    702\u001B[0m         \u001B[0;32mwhile\u001B[0m \u001B[0;32mTrue\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    703\u001B[0m             \u001B[0;32mtry\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 704\u001B[0;31m                 \u001B[0;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_sock\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mrecv_into\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mb\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m    705\u001B[0m             \u001B[0;32mexcept\u001B[0m \u001B[0mtimeout\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m    706\u001B[0m                 \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_timeout_occurred\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;32mTrue\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m/usr/local/Cellar/python@3.9/3.9.9/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ssl.py\u001B[0m in \u001B[0;36mrecv_into\u001B[0;34m(self, buffer, nbytes, flags)\u001B[0m\n\u001B[1;32m   1239\u001B[0m                   \u001B[0;34m\"non-zero flags not allowed in calls to recv_into() on %s\"\u001B[0m \u001B[0;34m%\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m   1240\u001B[0m                   self.__class__)\n\u001B[0;32m-> 1241\u001B[0;31m             \u001B[0;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mread\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mnbytes\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mbuffer\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m   1242\u001B[0m         \u001B[0;32melse\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m   1243\u001B[0m             \u001B[0;32mreturn\u001B[0m \u001B[0msuper\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mrecv_into\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mbuffer\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mnbytes\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mflags\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;32m/usr/local/Cellar/python@3.9/3.9.9/Frameworks/Python.framework/Versions/3.9/lib/python3.9/ssl.py\u001B[0m in \u001B[0;36mread\u001B[0;34m(self, len, buffer)\u001B[0m\n\u001B[1;32m   1097\u001B[0m         \u001B[0;32mtry\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m   1098\u001B[0m             \u001B[0;32mif\u001B[0m \u001B[0mbuffer\u001B[0m \u001B[0;32mis\u001B[0m \u001B[0;32mnot\u001B[0m \u001B[0;32mNone\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m-> 1099\u001B[0;31m                 \u001B[0;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_sslobj\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mread\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mlen\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mbuffer\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m   1100\u001B[0m             \u001B[0;32melse\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m   1101\u001B[0m                 \u001B[0;32mreturn\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_sslobj\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mread\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mlen\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
      "\u001B[0;31mKeyboardInterrupt\u001B[0m: "
     ]
    }
   ],
   "source": [
    "import os\n",
    "import sqlite3\n",
    "import tqdm\n",
    "\n",
    "BASE_DIR = '/Volumes/shared/murecom/intro/spotify/'\n",
    "DB_FILE = os.path.join(BASE_DIR, 'songs.db')\n",
    "PLAYLISTS_FILE = os.path.join(BASE_DIR, 'playlists.ndjson')\n",
    "SONGS_IDX_FILE = os.path.join(BASE_DIR, 'songs_ids.txt')\n",
    "\n",
    "if os.path.isfile(DB_FILE):\n",
    "    os.remove(DB_FILE)\n",
    "\n",
    "conn = sqlite3.connect(DB_FILE)\n",
    "c = conn.cursor()\n",
    "\n",
    "c.execute('CREATE TABLE IF NOT EXISTS songs (id text primary key, name text, artist text)')\n",
    "c.execute('CREATE INDEX IF NOT EXISTS name_idx on songs(name)')\n",
    "\n",
    "c.execute('CREATE TABLE IF NOT EXISTS playlists (id text primary key, name text, owner text)')\n",
    "# c.execute('CREATE INDEX IF NOT EXISTS name_idx on playlists(name)')\n",
    "\n",
    "c.execute(\n",
    "    'CREATE TABLE IF NOT EXISTS playlist_song (pid text, sid text, foreign key(pid) references playlists(id), foreign key(sid) references songs(id))')\n",
    "# c.execute('CREATE INDEX IF NOT EXISTS playlist_idx on playlist_song(pid)')\n",
    "# c.execute('CREATE INDEX IF NOT EXISTS song_idx on playlist_song(sid)')\n",
    "\n",
    "tracks_seen = set()\n",
    "with open(PLAYLISTS_FILE, 'w') as f_playlists:\n",
    "    with open(SONGS_IDX_FILE, 'w') as f_song_ids:\n",
    "        for playlist in tqdm.tqdm(list(playlists.values())):\n",
    "            f_playlists.write(json.dumps(playlist) + '\\n')\n",
    "\n",
    "            c.execute(\"INSERT OR REPLACE INTO playlists VALUES (?, ?, ?)\",\n",
    "                      (playlist[\"id\"], playlist[\"name\"], playlist[\"owner\"]))\n",
    "\n",
    "            track_ids = []\n",
    "            try:\n",
    "                for track in tracks_gen(sp, playlist):\n",
    "                    track_id = track['id']\n",
    "                    if not track_id:\n",
    "                        continue\n",
    "                    if not track_id in tracks_seen:\n",
    "                        c.execute(\"INSERT OR REPLACE INTO songs VALUES (?, ?, ?)\",\n",
    "                                  (track['id'], track['name'], track['artists'][0]['name']))\n",
    "                        c.execute(\"insert or replace into playlist_song values (?, ?)\",\n",
    "                                  (playlist['id'], track['id']))\n",
    "                    track_ids.append(track_id)\n",
    "            except SpotifyException as e:\n",
    "                print(\"error(tracks_gen): \", e)\n",
    "                continue\n",
    "            f_song_ids.write(' '.join(track_ids) + '\\n')\n",
    "            conn.commit()\n",
    "conn.commit()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "c.close()\n",
    "conn.close()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}